use rand::Rng;
use slint::{
    ModelRc, 
    VecModel, 
    Timer, 
    TimerMode,
};
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;

slint::include_modules!();

type Direction = (i32, i32);

enum GameState {
    Move,
    Grow,
    Die,
}

struct SnakeGame {
    row_size: i32,
    col_size: i32,
    snake: Rc<RefCell<Vec<SnakeSegment>>>,
    direction: Rc<RefCell<Direction>>,
    food: Rc<RefCell<Food>>,
    score: i32,
}

impl SnakeGame {
    pub fn new(ui: &AppWindow) -> Self {
        let row_size: i32 = ui.get_row_size();
        let col_size: i32 = ui.get_col_size();
        let game = Self {
            row_size, col_size,
            snake: Rc::new(RefCell::new(vec![SnakeSegment { x: 12, y: 8 }, SnakeSegment { x: 11, y: 8 }, SnakeSegment { x: 10, y: 8 }])),
            direction: Rc::new(RefCell::new((1, 0))),
            food: Rc::new(RefCell::new(Food{x: 3, y: 3})),
            score: 0,
        };

        game
    }

    pub fn get_next_state(&self) -> GameState {
        let head = self.snake.borrow()[0].clone();

        let hit_wall = head.x < 0 || head.x >= self.col_size || head.y < 0 || head.y >= self.row_size;
        let hit_self = self.snake.borrow().iter().skip(1).any(|seg| seg.x == head.x && seg.y == head.y);
        let hit_food = head.x == self.food.borrow().x && head.y == self.food.borrow().y;

        if hit_wall || hit_self {
            GameState::Die
        } else if hit_food {
            GameState::Grow
        } else {
            GameState::Move
        }
    }

    pub fn update_head(&mut self) {
        let (dx, dy) = *self.direction.borrow();
        let mut new_head = self.snake.borrow()[0].clone();
        new_head.x += dx;
        new_head.y += dy;
        self.snake.borrow_mut().insert(0, new_head);
    }

    pub fn update_food(&mut self) {
        *self.food.borrow_mut() = self.generate_valid_food();
    }

    pub fn move_(&mut self) {
        self.snake.borrow_mut().pop();
    }

    pub fn update_ui(&self, ui: &AppWindow) {
        let model = ModelRc::new(VecModel::from(self.snake.borrow().clone()));
        ui.set_food(self.food.borrow().clone());
        ui.set_snake(model);
        ui.set_score(self.score);
    }

    pub fn grown(&mut self) {
        self.score += 1;
    }

    fn generate_valid_food(&self) -> Food {
        let mut food = self.generate_random_food();
        while self.snake.borrow().iter().any(|s| s.x == food.x && s.y == food.y) {
            food = self.generate_random_food();
        }
        food
    }
    
    fn generate_random_food(&self) -> Food {
        let mut rng = rand::rng();
        Food { x: rng.random_range(0..self.col_size), y: rng.random_range(0..self.row_size)}
    } 
} 


fn main() -> Result<(), slint::PlatformError> {
    let ui = Rc::new(AppWindow::new()?);
    let game = Rc::new(RefCell::new(SnakeGame::new(&ui)));
    game.borrow().update_ui(&ui);

    // 注册键盘处理
    let direction = game.borrow().direction.clone();
    ui.on_key_pressed(move | event | {
        let key = event.text.chars().next().unwrap_or(' ');
        let new_dir = match key {
            'w' => Some((0, -1)),
            's' => Some((0, 1)),
            'a' => Some((-1, 0)),
            'd' => Some((1, 0)),
            _ => None,
        };

        if let Some(dir) = new_dir {
            *direction.borrow_mut() = dir;
        }
    });

    // 注册定时器
    let timer = Rc::new(Timer::default());

    let game_clone = game.clone();
    let ui_clone = ui.clone();
    let timer_clone = timer.clone();

    let timer_callback = move || {
        let game = game_clone.clone();
        let ui = ui_clone.clone();
        let timer = timer_clone.clone();

        game.borrow_mut().update_head();

        let next_state = game.borrow_mut().get_next_state();
        match next_state {
            GameState::Die => {
                timer.stop();
                ui.set_game_over(true);
            }
            GameState::Grow => {
                game.borrow_mut().grown();
                game.borrow_mut().update_food();
            }
            GameState::Move => {
                game.borrow_mut().move_();
            }
        }

        game.borrow().update_ui(&ui);
    };

    let timer_clone = timer.clone();
    ui.on_start_game(move || {
        timer_clone.start(TimerMode::Repeated, Duration::from_millis(200), timer_callback.clone());
    });

    let timer_clone = timer.clone();
    ui.on_pause_game(move || {
        timer_clone.stop();
    });

    let timer_clone = timer.clone();
    ui.on_continue_game(move || {
        timer_clone.restart();
    });

    ui.run()
}
